package libcore.eventbus2

import android.util.Log
import androidx.lifecycle.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import libcore.eventbus2.BusStore.events
import libcore.eventbus2.BusStore.stickyEvents

inline fun <reified T : BaseEvent> LifecycleOwner.registerEvent(
    isSticky: Boolean = true,
    noinline action: (t: T) -> Unit
) {
    EventBus.register(this, isSticky, action)
}

inline fun <reified T : BaseEvent> LifecycleOwner.postEvent(event: T, isSticky: Boolean = true) {
    this.lifecycleScope.launch {
        EventBus.postEvent(event, isSticky = false)
        if (isSticky) {
            EventBus.postEvent(event, isSticky = true)
        }
    }
}

object EventBus {
    private const val TAG = "BUS"

    fun clear() {
        // 应用程序退出后，清空event。避免由于粘性事件，app进行未死，再次打开register后又触发
        events.clear()
        stickyEvents.clear()
    }

    @Synchronized
    fun <T : BaseEvent> with(name: String, isSticky: Boolean): Event<T> {
        var event = if (isSticky) stickyEvents[name] else events[name]
        if (event == null) {
            event = Event<T>(name, isSticky)
            if (isSticky) {
                stickyEvents[name] = event
            } else {
                events[name] = event
            }
        }
        return event as Event<T>
    }

    inline fun <reified T : BaseEvent> register(
        lifecycleOwner: LifecycleOwner,
        isSticky: Boolean = true,
        noinline action: (t: T) -> Unit
    ) {
        with<T>(T::class.java.name, isSticky).register(lifecycleOwner, action)
    }

    suspend inline fun <reified T : BaseEvent> postEvent(event: T, isSticky: Boolean = true) {
        with<T>(T::class.java.name, isSticky).post(event)
    }


    class Event<T : BaseEvent>(private val key: String, private val isSticky: Boolean) :
        LifecycleObserver {

        // private mutable shared flow
        private val _events = MutableSharedFlow<T>(
            replay = if (isSticky) 1 else 0,
            extraBufferCapacity = Int.MAX_VALUE
        )

        // publicly exposed as read-only shared flow
        private val events = _events.asSharedFlow()

        /**
         * need main thread execute
         */
        fun register(lifecycleOwner: LifecycleOwner, action: (t: T) -> Unit) {
            lifecycleOwner.lifecycle.addObserver(this)
            lifecycleOwner.lifecycleScope.launch {
                events.collect {
                    try {
                        action(it)
                    } catch (e: Exception) {
                        Log.e(TAG, "KEY:%s---ERROR:%s".format(key, e.toString()))
                    }
                }
            }
        }

        /**
         * send value
         */
        suspend fun post(event: T) {
            event.id = System.currentTimeMillis()
            _events.emit(event)
        }

        /**
         * When subscriptionCount less than 0,remove event object in map
         */
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroy() {
            // 观察者销毁时，对于非粘性事件，如果flow的订阅者数为0，则移除event
            if (!isSticky && _events.subscriptionCount.value <= 0)
                BusStore.events.remove(key)
        }
    }
}

private object BusStore {
    val events = mutableMapOf<String, EventBus.Event<*>>()
    val stickyEvents = mutableMapOf<String, EventBus.Event<*>>()
}